package view.components;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

import controller.Controller;

import view.SLogoView;
import view.commands.Drawer;
import view.commands.ImageReader;

import model.turtle.Position;
import model.turtle.Turtle;

/**
 * Canvas for showing turtle and its trail
 * @author Yin
 */
public class TurtleCanvas extends JPanel //implements Observer
{
        private Controller myController;
        private BufferedImage myBufferedImage;
        private ImageReader myImageReader;
        private Drawer myDrawer;
        private Position myPosition;
        private List<List<Integer>> myAllX;
        private List<List<Integer>> myAllY;
        private double myDegree;
        
        private static final String IMAGE_LOCATION = "ImageLocation";
        private static final String TURTLE_CANVAS_SIZE = "TurtleCanvasSize";
        private static final String TURTLE_ERROR = "TurtleCanvasError";
        
        public TurtleCanvas(Controller controller) throws IOException
        {               
//              setBorder(BorderFactory.createRaisedBevelBorder());
                List<Integer> size = SLogoView.getSize(TURTLE_CANVAS_SIZE);
                try
                {
                        setPreferredSize(new Dimension(size.get(0), size.get(1)));
                }
                catch (Exception e)
                {
                        new ErrorCatcher(TURTLE_ERROR);
                }
                setBackground(Color.WHITE);
                setVisible(true);
                
                myController = controller;              
                
                //reset();
                myImageReader = new ImageReader();
            	myBufferedImage = myImageReader.execute(ComponentsFactory.getComponentsMap().get(IMAGE_LOCATION));
                myDrawer = new Drawer(myBufferedImage);
                update(new ArrayList<Turtle>());
        }
        
        /**
         * Update turtle information and paint turtle's new trail
         */
        public void update(List<Turtle> turtles)
        {                       
            myDrawer.execute(turtles);
            myAllX = myDrawer.getXs();
            myAllY = myDrawer.getYs();
            myPosition = myDrawer.getPosition();
            myDegree = myDrawer.getDegree();
            repaint();
        }
                
        /**
         * overwrite paint method
         */
        @Override
        public void paint(Graphics g)
        {
                super.paint(g);
                
                for (int i=0; i<myAllX.size(); i++)
                                g.drawPolyline(listToArray(myAllX.get(i)), 
                                                       listToArray(myAllY.get(i)), 
                                                       listToArray(myAllX.get(i)).length); // Treat trail in Turtle class as absolute positions
                
                myBufferedImage = myImageReader.execute(ComponentsFactory.getComponentsMap().get(IMAGE_LOCATION));
                rotateImage(Math.toRadians(myDegree));
                
                if (myDrawer.turtleIsShown())
                        g.drawImage(myBufferedImage, myPosition.getX(), myPosition.getY(), this);  // Show turtle
                
        }
        
        /**
         * rotate image
         * @param angle is the degree that want to rotate
         */
        protected void rotateImage(double angle)
        {
                if (myBufferedImage == null)
                    return; // Return if no image exists
                
                BufferedImage filteredBufImage = new BufferedImage(myBufferedImage.getWidth(this), myBufferedImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
                AffineTransform transform = new AffineTransform();
                transform.rotate(angle, myBufferedImage.getWidth(this)/2, myBufferedImage.getHeight(this)/2);
                AffineTransformOp imageOp = new AffineTransformOp(transform, null);
                imageOp.filter(myBufferedImage, filteredBufImage); // Filter image
                                                                   // Filtered image is stored in filteredBufImage
                myBufferedImage = filteredBufImage;
        }
        
        /**
         * convert an arraylist to int array
         * @param list
         * @return
         */
        protected int[] listToArray(List<Integer> list)
        {
                int[] result = new int[list.size()];
                for(int i=0; i<list.size(); i++)
                        result[i] = list.get(i);
                return result;
        }
}
